import 'dart:async';
import 'dart:io';

import 'package:device_info_plus/device_info_plus.dart';
import 'package:geolocation/models/position.dart';
import 'package:get/get.dart';
import 'package:o2oa_all_platform/common/extension/index.dart';

import '../../pages/common/face_check_page/index.dart';
import '../api/index.dart';
import '../models/index.dart';
import '../utils/index.dart';
import '../widgets/index.dart';
import 'shared_preference_service.dart';

/// 极速打卡功能
class FastCheckInService {
  // 单例
  static final FastCheckInService instance = FastCheckInService._internal();
  factory FastCheckInService() => instance;
  FastCheckInService._internal();

  // 定时器
  Timer? _timer;

  /// geolocator 定位
  GeolocatorHelper? _geoHelper;

  /// 当前定位信息
  GeoPosition? _myPosition;

  // 上班是否开启极速打卡
  bool _onDutyFastCheckInEnable = false;
  // 下班是否开启极速打卡
  bool _offDutyFastCheckInEnable = false;
  // 是否正在运行中
  bool _isRunning = false;
  // 运行时间 超过 2 分钟就结束
  int _runningTime = 0;
  // 工作地点
  final List<WorkPlaceList> _workplaceList = [];
  WorkPlaceList? _nearLeastWorkplace; // 最近的打卡地点
  bool _isInCheckInPositionRange = false; // 最近的打卡地点是否在打卡范围内
  AttendanceV2Record? _currentCheckItem; // 当前打卡对象
  bool _isPosting = false;

  /// 开始运行极速打卡
  Future<void> start() async {
    if (!Platform.isAndroid && !Platform.isIOS) return;
    if (_isRunning) return;
    OLogger.i('开始极速打卡！');
    _isRunning = true;
    _onDutyFastCheckInEnable = SharedPreferenceService.to
        .getBool(SharedPreferenceService.attendanceOnDutyFastCheckinKey);
    _offDutyFastCheckInEnable = SharedPreferenceService.to
        .getBool(SharedPreferenceService.attendanceOffDutyFastCheckinKey);
    if (!_onDutyFastCheckInEnable && !_offDutyFastCheckInEnable) {
      OLogger.w('没有配置极速打卡!');
      _isRunning = false;
      return;
    }
    _startTimer();
    _loadPreCheckInData();
    // final config = await AttendanceAssembleControlService.to.config();
    // if (config != null &&
    //     (config.onDutyFastCheckInEnable == true ||
    //         config.offDutyFastCheckInEnable == true)) {

    // } else {
    //   OLogger.d('没有配置极速打卡!');
    //   _isRunning = false;
    // }
  }

  /// 结束运行极速打卡
  void stop() {
    OLogger.i('结束极速打卡！');
    _timer?.cancel();
    _timer = null;
    _geoHelper?.stopLocation();
    _geoHelper = null;
    _myPosition = null;
    _nearLeastWorkplace = null;
    _currentCheckItem = null;
    _runningTime = 0;
    _isRunning = false;
  }

  /// 开始定位
  Future<void> _startLocation() async {
    // 初始化定位程序
    _geoHelper = GeolocatorHelper(callback: (position) {
      _myPosition = position;
      _calNearestWorkplace();
    });
    _geoHelper?.startLocation();
  }

  /// 开始计时 2 分钟 没有运行结束就 stop
  void _startTimer() {
    /// 间隔5秒
    _timer = Timer.periodic(const Duration(milliseconds: 5000), (timer) {
      _runningTime += 5000;
      if (_runningTime >= 2 * 60 * 1000) {
        stop();
      }
    });
  }

  /// 获取打卡初始化数据
  /// 先判断数据业务 除了 固定班制 排班制 其他不进行自动打卡
  Future<void> _loadPreCheckInData() async {
    final data = await AttendanceAssembleControlService.to.loadPreCheckInData();
    if (data == null) {
      stop();
      return;
    }
    bool needCheck = data.canCheckIn ?? false; //今天是否还需要打卡
    if (needCheck) {
      // 打卡记录
      final checkItemList = data.checkItemList ?? [];
      // 是否最后一条已经打卡过的数据
      _currentCheckItem = checkItemList
          .firstWhereOrNull((element) => element.checkInResult == 'PreCheckIn');
      // 固定班制 排班制 并且有班次 id 才能进行极速打卡
      if (_currentCheckItem != null &&
          _currentCheckItem?.shiftId?.isNotEmpty == true &&
          (_currentCheckItem?.groupCheckType == "1" ||
              _currentCheckItem?.groupCheckType == "3")) {
        needCheck = true;
      } else {
        needCheck = false;
      }
    }
    // 如果不能打卡了 就结束
    if (!needCheck) {
      OLogger.i("今天无需打卡， 全部停止。");
      stop();
    } else {
      // 开始启动定位
      _startLocation();
      // 开始查询工作场所数据
      if (data.workPlaceList != null && data.workPlaceList!.isNotEmpty) {
        _workplaceList
          ..clear()
          ..addAll(data.workPlaceList!);
        // 计算最近的打卡地点
        _calNearestWorkplace();
      }

      _tryCheckIn();
    }
  }

  /// 尝试极速打卡
  void _tryCheckIn() {
    if (_myPosition == null || _workplaceList.isEmpty) {
      OLogger.e('可能还未定位到。。。。');
      return;
    }
    if (_nearLeastWorkplace != null &&
        _currentCheckItem != null &&
        _isInCheckInPositionRange) {
      // 上班打卡
      if ((_currentCheckItem?.checkInType == 'OnDuty' &&
              _onDutyFastCheckInEnable) ||
          (_currentCheckItem?.checkInType == 'OffDuty' &&
              _offDutyFastCheckInEnable)) {
        // 是否在打卡限制时间内
        final dutyTime = _currentCheckItem?.preDutyTime ?? '';
        final preBeforeTime = _currentCheckItem?.preDutyTimeBeforeLimit ?? '';
        final preAfterTime = _currentCheckItem?.preDutyTimeAfterLimit ?? '';
        if (!_checkLimitTime(_currentCheckItem?.checkInType ?? '', dutyTime,
            preBeforeTime, preAfterTime)) {
          OLogger.e("不在限制时间内！！！！");
          stop();
          return;
        }
        _postCheckIn(_currentCheckItem!, _nearLeastWorkplace!.id!);
      } else {
        OLogger.i("当前打卡类型：${_currentCheckItem?.checkInType} 不允许极速打卡！");
        stop();
      }
    }
  }

  // 打卡
  Future<void> _postCheckIn(
      AttendanceV2Record record, String workPlaceId) async {
    if (_isPosting) return;
    _isPosting = true;
    final result = await checkinPost(
      '${_myPosition!.latitude}',
      '${_myPosition!.longitude}',
      '${_myPosition!.address}',
      record.checkInType ?? '',
      'FAST_CHECK',
      workPlaceId: workPlaceId,
      isExternal: false,
      id: record.id ?? '',
    );
    if (result) {
      OLogger.i('极速打卡成功！');
      final now = DateTime.now().ymdhms();
      // 发送通知
      O2OverlayEntryDialog.instance.openFastCheckInDialog(
          'attendance_fast_checkin_notify_title'.tr,
          'attendance_fast_checkin_notify_content'.trArgs([now]));
      stop();
    }
    _isPosting = false;
  }

  /// 是否有打卡时间限制
  bool _checkLimitTime(String checkInType, String dutyTime,
      String preDutyTimeBeforeLimit, String preDutyTimeAfterLimit) {
    final now = DateTime.now();
    final today = now.ymd();
    final dutyTimeDate = DateTime.tryParse('$today $dutyTime:00');
    if (dutyTimeDate == null) {
      OLogger.e('时间解析出差，$today $dutyTime');
      return false;
    }
    // 极速打卡开始时间
    var fastCheckInBeforeLimit = dutyTimeDate;
    if (checkInType == 'OnDuty') {
      //上班前一个小时
      fastCheckInBeforeLimit = dutyTimeDate.addMinutes(-60);
    }

    // 极速打卡结束时间
    var fastCheckInAfterLimit = dutyTimeDate.addMinutes(60); // 下班后1个小时
    if (checkInType == 'OnDuty') {
      fastCheckInAfterLimit = dutyTimeDate;
    }

    if (preDutyTimeBeforeLimit.isNotEmpty) {
      final beforeTime = DateTime.tryParse("$today $preDutyTimeBeforeLimit:00");
      if (beforeTime != null && fastCheckInBeforeLimit.isBefore(beforeTime)) {
        fastCheckInBeforeLimit = beforeTime;
      }
    }
    if (preDutyTimeAfterLimit.isNotEmpty) {
      final afterTime = DateTime.tryParse("$today $preDutyTimeAfterLimit:00");
      if (afterTime != null && fastCheckInAfterLimit.isAfter(afterTime)) {
        fastCheckInAfterLimit = afterTime;
      }
    }
    OLogger.d(
        "打卡时间，$dutyTimeDate 极速打卡开始时间：$fastCheckInBeforeLimit 极速打卡结束时间： $fastCheckInAfterLimit");
    if (fastCheckInBeforeLimit.isBefore(now) &&
        fastCheckInAfterLimit.isAfter(now)) {
      return true;
    }
    return false;
  }

  /// 计算最近的打卡地点
  Future<void> _calNearestWorkplace() async {
    if (_myPosition == null) {
      OLogger.e('还没有定位成功！');
      return;
    }
    _isInCheckInPositionRange = false;
    if (_workplaceList.isNotEmpty) {
      for (final workplace in _workplaceList) {
        OLogger.d('工作地点： ${workplace.latitude}  ${workplace.longitude}');
        double startLatitude = _myPosition?.latitude ?? 0;
        double startLongitude = _myPosition?.longitude ?? 0;
        final lngLat = workplace.getLngLat();
        double endLatitude = lngLat[1];
        double endLongitude = lngLat[0];
        if (startLatitude != 0 &&
            startLongitude != 0 &&
            endLatitude != 0 &&
            endLongitude != 0) {
          OLogger.d(
              '坐标  workplace endLatitude:$endLatitude endLongitude:$endLongitude myPosition startLatitude: $startLatitude startLongitude: $startLongitude');
          final distance = _geoHelper?.distanceInMeters(
                  startLatitude, startLongitude, endLatitude, endLongitude) ??
              0;
          int range = workplace.errorRange ?? 100; // 默认 100 米
          OLogger.d('距离计算：$distance $range');
          if (distance != 0 && distance <= range) {
            // 找到了范围内的打卡地点
            _nearLeastWorkplace = workplace;
            _isInCheckInPositionRange = true;
            _tryCheckIn();
            break;
          }
        } else {
          OLogger.e(
              ' 错误的坐标  workplace endLatitude:$endLatitude endLongitude:$endLongitude myPosition startLatitude: $startLatitude startLongitude: $startLongitude');
        }
      }
      OLogger.d('找到最近的打卡点? ${_nearLeastWorkplace?.placeName ?? '无'}');
    }
  }

  /// 提交打卡
  Future<bool> checkinPost(String latitude, String longitude, String addrStr,
      String checkType, String sourceType,
      {String workPlaceId = '',
      String signDesc = '',
      String id = '',
      bool isExternal = false,
      String workAddress = '',
      bool showLoading = false}) async {
    if (isExternal) {
      // 外勤打开
      if (signDesc.isEmpty) {
        Loading.toast('attendance_outside_need_desc'.tr);
        return false;
      }
    }
    final check = await _faceDetection();
    if (!check) {
      OLogger.e('message: 人脸识别失败！');
      Loading.toast('attendance_setting_fast_checkin_fail'.tr);
      return false;
    }
    if (showLoading) Loading.show();
    var post = CheckPost(
        recordId: id,
        checkInType: checkType,
        workPlaceId: workPlaceId,
        fieldWork: isExternal,
        signDescription: signDesc,
        latitude: latitude,
        longitude: longitude,
        recordAddress: addrStr,
        sourceType: sourceType);
    var deviceType = SharedPreferenceService.to
        .getString(SharedPreferenceService.currentDeviceTypeKey);

    final checkScript =
        await _externalCheck(id, deviceType, checkType, isExternal);
    if (!checkScript.check) {
      if (showLoading) Loading.dismiss();
      OLogger.e('打卡检查脚本失败, ${checkScript.deviceId} ${checkScript.message}');
      var message = 'attendance_checkin_external_check_error'.tr;
      if (checkScript.message != null && checkScript.message!.isNotEmpty) {
        message = checkScript.message!;
      }
      Loading.toast(message);
      return false;
    }
    post.sourceDevice = checkScript.deviceId;
    var iddata = await AttendanceAssembleControlService.to.checkInPostV2(post);
    if (iddata != null) {
      if (showLoading) Loading.dismiss();
      return true;
    }
    return false;
  }

  /// 人脸识别
  Future<bool> _faceDetection() async {
    final config = await AttendanceAssembleControlService.to.config();
    if (config?.faceDetectionEnable == true) {
      OLogger.d('启用人脸识别！');
      final check = await _confirmFaceDetection();
      if (!check) {
        return false;
      }
      final result = await FaceCheckPagePage.startDetection();
      if (result != null &&
          result is FaceDetectionResult &&
          result.result == true) {
        return true;
      } else {
        return false;
      }
    }
    OLogger.i('未启用人脸识别！');
    return true;
  }
  // 提示打开人脸识别
  Future<bool> _confirmFaceDetection() async {
    final context = Get.context;
    if (context == null) {
      return false;
    }
    final completer = Completer<bool>();
    O2UI.showConfirm(context, 'attendance_setting_fast_checkin_confirm'.tr, okPressed: () => completer.complete(true), cancelPressed: () => completer.complete(false));
    return completer.future;
  }

  /// 外部脚本 检查是否能打卡
  /// 需要在 appstyle 配置文件 【 extendParam 】中 配置 attendanceCheckScript字段，内容是invoke脚本名称
  Future<({bool check, String deviceId, String? message})> _externalCheck(
      String recordId,
      String deviceType,
      String checkInType,
      bool fieldWork) async {
    var deviceId = await _getDeviceId();
    if (deviceId.isEmpty) {
      OLogger.e('没有获取到设备 id！');
    }
    deviceId = '${deviceType}_$deviceId';
    final script =
        ProgramCenterService.to.extendParam()['attendanceCheckScript'];
    if (script == null || script is! String) {
      OLogger.i('没有打卡检查脚本！');
      return (check: true, deviceId: deviceId, message: null);
    }
    final body = {
      "recordId": recordId,
      "checkInType": checkInType,
      "fieldWork": fieldWork,
      "person": O2ApiManager.instance.me().distinguishedName,
      "device": deviceId
    };
    final result = await ProgramCenterService.to.executeScript(script, body);
    if (result != null) {
      OLogger.d('打卡检查脚本结果：$result');
      final s = result['result'];
      final message = (result['message'] != null && result['message'] is String)
          ? (result['message'] as String)
          : '';
      if (s != null && s is bool && s == false) {
        return (check: false, deviceId: deviceId, message: message);
      }
    }
    return (check: true, deviceId: deviceId, message: null);
  }

  // 设备信息插件
  final DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin();

  Future<String> _getDeviceId() async {
    String deviceId = SharedPreferenceService.to
        .getString(SharedPreferenceService.attendanceDeviceIdSpKey);
    if (deviceId.isNotEmpty && deviceId != 'unknown') {
      OLogger.d('获取到本地存储的设备 id：$deviceId');
      return deviceId;
    }
    if (Platform.isIOS) {
      var iosDeviceInfo = await deviceInfoPlugin.iosInfo;
      deviceId =
          iosDeviceInfo.identifierForVendor ?? 'unknown'; // unique ID on iOS
    } else if (Platform.isAndroid) {
      var androidDeviceInfo = await deviceInfoPlugin.androidInfo;
      deviceId = androidDeviceInfo.id; // unique ID on Android
    } else {
      deviceId = 'unknown';
    }
    SharedPreferenceService.to
        .putString(SharedPreferenceService.attendanceDeviceIdSpKey, deviceId);
    OLogger.d('获取到设备 id：$deviceId');
    return deviceId;
  }
}
